package com.king.alg;

import com.king.pojo.Point;

import java.util.*;

public class MultiFitAlgorithms {

    /**
     * 设置权重，不包括首尾
     *
     * @param pointList 已经经过循环Douglas-Peucker算法处理后的点列
     */
    private static void setWeight(List<Point> pointList) {
        Map<Integer, Double> weightMap = TurnFitExtendAlgorithms.calcWeight(pointList);
        weightMap.forEach((i, d) -> pointList.get(i).addWeight(d));
    }

    /**
     * 循环设置权重，为了将首尾设置权重
     *
     * @param pointList 已经经过循环Douglas-Peucker算法处理后的点列
     */
    private static void setWeightCyc(List<Point> pointList) {
        int len = pointList.size();

        for (int i = 0; i < len; i++) {
            setWeight(pointList);
            pointList.add(pointList.remove(0));
        }

        pointList.add(pointList.remove(0));
    }


    private static class IndexWeightBean implements Comparable<IndexWeightBean> {
        public int index;
        public double weight;

        public IndexWeightBean(int index, double weight) {
            this.index = index;
            this.weight = weight;
        }

        @Override
        public int compareTo(IndexWeightBean o) {
            return (int) (this.weight - o.weight);
        }
    }

    /**
     * 选出前几个权重最大的点的索引
     *
     * @param pointList 已经经过setWeightCyc算法处理后的点列
     * @param n         前几个最大的点的个数
     * @return 索引
     */
    private static List<Integer> chooseMaxN(List<Point> pointList, int n) {
        List<IndexWeightBean> indexWeightBeanList = new LinkedList<>();
        int len = pointList.size();

        for (int i = 0; i < len; i++) {
            indexWeightBeanList.add(new IndexWeightBean(i, pointList.get(i).getAvgWeight()));
        }

        Collections.sort(indexWeightBeanList);
        List<Integer> result = new LinkedList<>();

        for (int i = 0; i < n; i++) {
            result.add(indexWeightBeanList.get(i).index);
        }

        return result;
    }

    private static double getDisByTowPolygon(List<Point> list1, List<Point> list2) {
        double result = 0;

        for (int i = 0; i < list1.size(); i++) {
            result += GeometryAlgorithms.norm(list1.get(i), list2.get(i));
        }

        return result;
    }

    private static class ListPointDis implements Comparable<ListPointDis> {
        List<Point> list;
        double dis;

        public ListPointDis(List<Point> list, double dis) {
            this.list = list;
            this.dis = dis;
        }

        @Override
        public int compareTo(ListPointDis o) {
            return (int) (this.dis - o.dis);
        }
    }

    private static List<Point> alignment(List<Point> template, List<Point> toBeAligned) {
        Set<ListPointDis> set = new HashSet<>();
        List<Point> oldList = toBeAligned;
        List<Point> newList;
        int times = 0;
        while (true) {
            for (int i = 0; i < template.size(); i++) {
                set.add(new ListPointDis(oldList, getDisByTowPolygon(template, oldList)));
                newList = new LinkedList<>(oldList);
                newList.add(newList.remove(0));
                oldList = newList;
            }
            if (times == 1) return Collections.min(set).list;
            newList = new LinkedList<>(oldList);
            Collections.reverse(newList);
            oldList = newList;
            times++;
        }
    }

    private static List<Point> polysAvg(List<List<Point>> pointLists) {
        List<Point> result = new LinkedList<>();
        int length = pointLists.get(0).size();
        int total = pointLists.size();
        for (int i = 0; i < length; i++) {
            double x = 0, y = 0;
            for (List<Point> pj : pointLists) {
                Point pji = pj.get(i);
                x += pji.getX();
                y += pji.getY();
            }
            result.add(new Point(x / total, y / total));
        }
        return result;
    }

    public static List<List<Point>> alignCount(List<List<Point>> pointLists) {
        List<List<Point>> result = new LinkedList<>();
        int minLen = Integer.MAX_VALUE;

        for (List<Point> pointList : pointLists) {
            int currLen = pointList.size();
            if (currLen < minLen) {
                minLen = pointList.size();
            }
            setWeightCyc(pointList);
        }

        for (List<Point> pointList : pointLists) {
            List<Integer> maxNIndex = chooseMaxN(pointList, minLen);
            List<Point> polygonInclude = PolygonAlgorithms.createPolygonInclude(pointList, maxNIndex);
            result.add(polygonInclude);
        }

        return result;
    }

    /**
     * 根据多次重复测量得出拟合的结果
     *
     * @param pointLists 多次重复测量
     * @return 拟合的结果
     */
    public static List<Point> multiAvg(List<List<Point>> pointLists) {
        List<List<Point>> alignCountPointLists = alignCount(pointLists);
        List<List<Point>> aligned = new LinkedList<>();
        aligned.add(alignCountPointLists.get(0));
        for (int i = 1; i < alignCountPointLists.size(); i++) {
            aligned.add(alignment(alignCountPointLists.get(0), alignCountPointLists.get(i)));
        }
        return polysAvg(aligned);
    }
}
